export type FormListener = (name: string) => void;

export type FormValidator = (value: any, values: any) => void | Promise<void>;

export type FormRules = { [key: string]: FormValidator };

export type FormErrors = { [key: string]: any | undefined };

export class FormStore {
  private listeners: FormListener[] = [];
  private values: Object;
  private rules: FormRules;
  private errors: FormErrors;

  constructor(defaultValues = {}, rules = {}) {
    this.values = defaultValues;

    // 表单校验规则
    this.rules = rules;
    // 事件回调列表
    this.listeners = [];

    this.errors = {};
  }

  public subscribe(listener: FormListener, name: string) {
    this.listeners.push(listener);
    if (!this.errors[name]) {
      this.errors[name] = undefined;
    }
    if (this.values[name]) {
      this.set(name, this.values[name]);
    }
    // 返回一个用于取消订阅的函数
    return () => {
      const index = this.listeners.indexOf(listener);
      if (index > -1) this.listeners.splice(index, 1);
    };
  }

  // 通知表单变动，调用所有listener
  private notify(name: string) {
    this.listeners.forEach((listener) => listener(name));
  }

  // 获取表单值
  public get(name) {
    // 如果传入name，返回对应的表单值，否则返回整个表单的值
    return name === undefined ? this.values : this.values[name];
  }

  // 设置表单值
  public set(name, value) {
    // 如果指定了name
    if (typeof name === 'string') {
      // 设置name对应的值
      this.values[name] = value;
      // 执行表单校验，见下
      this.validate(name);
      // 通知表单变动
      this.notify(name);
    } else if (name) {
      // 批量设置表单值
      const values = name;
      Object.keys(values).forEach((key) => this.set(key, values[key]));
    }
  }

  public error(...args: any[]) {
    const [name, value] = args;
    if (args.length === 2) {
      if (value) {
        this.errors[name] = value;
      } else {
        delete this.errors[name];
      }
    }

    return this.errors[name];
  }

  // 用于表单校验
  public validate(name?: string) {
    if (name) {
      // 根据name获取校验函数
      const validator = this.rules[name];
      // 执行校验函数得到结果
      const result = validator ? validator(name, this.values) : true;

      this.error(name, result === true ? undefined : result);
    } else {
      Object.keys(this.errors).forEach((item) => this.validate(item));
      this.notify('*');
    }
  }
}
